using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LogicalComponent.util;
using LogicalComponent.util.exception;

namespace LogicalComponent.rules.grid
{
    /// <summary>
    /// Class that implements UI independant logic for grids
    /// </summary>
    /// <typeparam name="T">The Tile class for this grid</typeparam>
    public class GridLogic<T> : IGrid<T> where T : ITile
    {
        List<ITile> IGrid.AllTiles{ get { return this.AllTiles.Cast<ITile>().ToList(); } }
        TileFactory<T> factory;
        Dictionary<Coordinate, T> tiles;

        /// <inheritdoc/>
        public List<T> AllTiles 
        {
            get
            {
                List<T> allTiles = new List<T>();
                foreach (T tile in this.tiles.Values)
                {
                    allTiles.Add(tile);
                }
                return allTiles;
            }
        }

        public GridLogic(TileFactory<T> factory)
        {
            this.factory = factory;
            this.tiles = new Dictionary<Coordinate, T>();
        }

        /// <summary>
        /// Creates a new GridLogic using a factory for the tiles.
        /// </summary>
        /// <param name="width">The width(x) of the grid</param>
        /// <param name="length">The length(z) of the grid</param>
        /// <param name="factory">The factory that will provide the tiles</param>
        public GridLogic(int width, int length, TileFactory<T> factory) : this(factory)
        {
            for (int i = 0; i < width; i++)
            {
                for (int j = 0; j < length; j++) 
                {
                    this.tiles[new Coordinate(i, j)] = factory.MakeTile(i, j);
                }
            }
        }
        
        /// <inheritdoc/>
        ITile IGrid.GetTile(int x, int z) { return this.GetTile(x, z); }

        /// <inheritdoc/>
        public T GetTile(int x, int z)
        {
            return this.GetTile(new Coordinate(x, z));
        }

        /// <inheritdoc/>
        ITile IGrid.GetTile(Coordinate c) { return this.GetTile(c); }

        /// <inheritdoc/>
        public T GetTile(Coordinate c) 
        {
            T tile = default(T);
            this.tiles.TryGetValue(c, out tile);
            return tile;
        }

        /// <inheritdoc/>
        public void SetTile(int x, int z, T tile)
        {
            this.SetTile(new Coordinate(x, z), tile);
        }

        /// <inheritdoc/>
        public void SetTile(Coordinate c, T tile) 
        {
            this.tiles[c] = tile;
        }

        /// <inheritdoc/>
        public bool IsOnGrid(Coordinate c)
        {
            return this.tiles.ContainsKey(c);
        }

        /// <inheritdoc/>
        public bool IsOnGrid(int x, int z)
        {
            return this.IsOnGrid(new Coordinate(x, z));
        }

        /// <inheritdoc/>
        public void CreateTile(Coordinate c)
        {
            this.tiles[c] = this.factory.MakeTile(c.X, c.Z);
        }

        /// <inheritdoc/>
        public void CreateTile(int x, int z)
        {
            this.CreateTile(new Coordinate(x, z));
        }

        /// <inheritdoc/>
        public void DestroyTile(Coordinate c)
        {
            if (!this.tiles.ContainsKey(c))
            {
                throw new LogicalUserError("Attempted to destroy a grid tile that does not exist");
            }
            this.tiles.Remove(c);
        }

        /// <inheritdoc/>
        public void DestroyTile(int x, int z)
        {
            this.DestroyTile(new Coordinate(x, z));
        }
    }
}
